home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
prog
/
pistol.zip
/
PISTOL.DOC
< prev
next >
Wrap
Text File
|
1987-08-20
|
39KB
|
1,249 lines
*********************************************************
* *
* PISTOL-Portably Implemented Stack Oriented Language *
* Version 1.3 *
* (C) 1982 by Ernest E. Bergmann *
* Physics, Building #16 *
* Lehigh Univerisity *
* Bethlehem, Pa. 18015 *
* *
* Permission is hereby granted for all reproduction and *
* distribution of this material provided this notice is *
* is included. *
* *
*********************************************************
BRIEF, PRELIMINARY DOCUMENTATION ON PISTOL 1.3:
(updated February 24, 1982)
PISTOL is a Portably Implemented Stack Oriented
Language that has been developed for general use and
experimentation. It is at an early developmental stage and so
may have lots of "bugs". I would appreciate any comments and
suggestions. For the DEC-20, it has been implemented in
PASCAL; for CP/M, it has been implemented with the BD Software
C compiler v1.4.
PISTOL resembles an HP calculator in many ways. It
uses a (parameter) stack to pass numbers between routines. The
language is "RPN", that is Reverse Polish Notation. At
present, all arithmetic is performed in integer form. This
language was inspired by and has evolved from two mini- and
micro- based languages, FORTH (Charles Moore, 1970) and STOIC
(MIT and Harvard Biomedical Engineering Center, 1977).
GETTING STARTED
======= =======
Before describing the language further, this is how to
"bring it up":
type
DIR CORE1 to see if this file exists, otherwise, type
DIR PBASE to make sure this file exists.
type
EXE PISTOL.PAS
(and for the DEC-20):
link TTY: to the input.
PISTOL should come up with:
***PISTOL 1.3***
X>
To make it smarter, it can be "educated" with a set of
useful definitions already prepared in a file named, CORE1 or
PBASE ; to do this type:
X> 'CORE1 RESTORE (only if CORE1 exists!)
or
==
X> 'PBASE LOAD (if PBASE exists; this is much slower)
X> 'CORE1 COREDUMP (creates CORE1 for future use)
TUTORIAL:
=========
Before discussing some examples and features, it is
probably best to understand the "prompt". The prompt is what a
program types at the beginning of the line when it is awaiting
input from the user. The prompt supplied by PISTOL can be
several characters long and it is to inform or remind the user
about the "state" of the program. If a number appears at the
beginning of the prompt, it signifies that the parameter stack
is not empty. Next, a letter is displayed (such as "X") that
indicates the current number base used for i/o ("X", the Roman
numeral for "10", signifies decimal). After this letter there
may be other characters or symbols that are used to indicate if
you are in the middle of some syntactical construction.
Finally, the ">" completes the prompt expression. In the
examples supplied above and below we have attempted to suggest
the typical prompts that one might expect to see.
After educating PISTOL by loading PBASE or by restoring
CORE1, the system is "smarter" you can try the following
examples:
X> 1 2 23 STACK
The system takes each number as encountered and places them on
the stack. The word, STACK , prints the current contents of
the stack without changing the stack in any way.
Now try typing:
3X> + STACK
This should result in the addition of the top two members of
the stack (2 and 23) and placing the result (answer 25) back on
the stack. The word STACK then displays the current contents of
the stack.
Now try typing:
2X> * stack
The top two items of the stack will be multiplied together and
the result left on the stack. The word, "stack" is interpreted
as STACK, and we see that the only thing in the stack is the
answer 25.
To disable the automatic interpretation of lowercase
as uppercase, type:
X> RAISE OFF (or "raise off")
and successive lines will be interpreted without conversion of
lowercase to uppercase. To revert to conversion, type:
X> RAISE ON
There are a number of different options that can be
invoked such as ECHO ON and ECHO OFF which control the listing
of files read in by the '<filename> LOAD operation. One can
use CONSOLE ON and CONSOLE OFF to determine what reaches the
terminal. An error condion will automatically restore output
to the terminal. LIST ON and LIST OFF will determine what
output will also be routed to the output list file(of course a
listfile has to be declared first). This is useful to have a
more permanent record of what happened during a session. There
is also a SHOWCODE and a NOSHOWCODE which will show the results
of compiling each input line (that is for those who want or
like to know what is going on behind the scenes).
On the DEC-20 (and also in CP/M) one can exit from
PISTOL by using a control-C, but a more "refined" way is to
type the word, BYE, which will return you to the operating
system.
PISTOL can examine the contents of its own (virtual)
memory. The W@ ("fetch") operator is similar in spirit to the
PEEK function in BASIC. To place the contents of a memory
location on the stack one simply places the address on stack
and then types W@ . For example, if we want to place the
current value of RADIX, the base used in all I/O number
conversion, we need to find the value stored in the RAM
location at -1 (i.e. what is RAM[-1]?) we can type:
X> -1 W@
which will place the desired information on stack. The inverse
operation, which is more dangerous(because a mistake can crash
the program), is to store a new value in the RADIX, hence
change the number base. The operator, W! ("word store"),
performs this function(for this example we suppose that the
RADIX address is -1, usually, it is not):
X> 16 -1 W!
would convert PISTOL to converse in hexadecimal.
Of course it is aukward to remember constants, such as
the address of RADIX so we can define such constants by:
X> -1 'RADIX CONSTANT
After such a CONSTANT definition we can redo the examples that
use W@ and W! by (RADIX is in fact already defined in PBASE):
X> RADIX W@
and
X> 16 RADIX W!
As an additional convenience the user can define space
for variables with the word, VARIABLE. For example, If you
wish to define a variable with the initial value 0 and named
ANSWER, you should type:
X> 0 'ANSWER VARIABLE
Later, if you wish to perform the PASCAL statement:
ANSWER := ANSWER + 1;
you would write the PISTOL code:
X> ANSWER W@ 1 + ANSWER W!
Notice that you MUST choose an initial value in VARIABLE and,
of course, CONSTANT definitions.
Just like the convenience feature of PASCAL,
for which one can use SUCC(ANSWER) to increment ANSWER
by one, there exists an analogous operator (or you can
define your own!):
X> ANSWER 1+W!
The language has many resources beyond those presented
so far. Perhaps the most significant is the ability to define
new words or definitions to make the language increasingly
smarter. Try examining the contents of the file, PBASE , to see
how the definitions may be formed. As a complex example, the
word, = is defined whose function is to take the top item off
of the stack and print its value. For example, typing:
X> 8 8 + =
will cause the system to respond with the answer: 16. Note
that this calculation causes no net change to the stack(it is
the presence of a number before "X>" that indicates the number
of items in the stack at that moment); its contents before the
first "8" was typed is the same as its contents after the
system responds with 16. Thus the definition of the word = has
increased the convenience of the system for arithmetic
calculations.
The system can and does handle strings. Suppose you
would like the system to output "HELLO". Try typing:
X> 'HELLO MSG
The system will take the 'HELLO as a string to be placed upon
the stack. The MSG ,"message" takes the top item off of the
stack, assumes it to be a string, and prints it. The stack
contains the same stuff after MSG is executed as before the
'HELLO was typed.
Useful I/O words are CR which will output a carriage
return and line feed sequence. The word SPACE will output a
space. And the word SPACES will pop the top of stack to obtain
the number of spaces to be output. For example:
X> 'HELLO MSG SPACE 'HELLO MSG CR 5 SPACES 'BYE MSG
should produce:
HELLO HELLO
BYE
Standard output would be most tedious if we could not
create definitions to speed up programming. Here is a humorous
example:
X> 'HELLO : 'HELLO, MSG SPACE 'YOURSELF! CR ;
The use of : and ; provide the means to define a new word
"HELLO" Later, you can type:
X> HELLO
and the system will respond:
HELLO, YOURSELF!
Thus we see that the pair of symbols, ":" and ";"
delineate a structure used to make definitions. The material
in between the two symbols becomes the definition of the word
whose name is the string that was lastly placed on the stack
before the ":".
One can create strings with embedded blanks and tabs up
to 127 characters long by using double quotes to delineate both
ends of the string. For example, the word, HELLO, defined
above, could have been defined:
'HELLO : "HELLO, YOURSELF!" MSG CR ;
Even with RAISE ON, lowercase characters within double
quotation marks will not be converted.
There are other types of structures. The pair of
words, DO and LOOP permit an iterative structure. They use the
top two quantities on the stack as limits of iteration. So:
X> nn n DO ... LOOP
is equivalent to the PASCAL structure:
FOR I := n TO (nn-1) DO
BEGIN ... END;
To place the current value of the iteration variable on the
stack one uses the word, "I" . Here is an example that you can
try:
X> 'COUNTING : CR 1 + 1 DO I = SPACE LOOP ;
X> 10 COUNTING
and see PISTOL counting to 10.
An alternative terminating word to this structure is
+LOOP. If one uses in PISTOL:
nn n DO ... m +LOOP
one simulates the BASIC structure:
100 FOR I=n TO (nn-1) STEP m
.
.
.
200 NEXT I
PISTOL supports a conditional structure of the form:
IF ... ELSE .... THEN
When the IF is encountered the top of the stack is used as a
boolean variable; it is considered false if equal to zero and
true otherwise (as in LISP). If true, the actions "..." that
are bracketed between IF and ELSE are carried out; then program
flow skips to what follows THEN. Whereas, if the top of the
stack was false, the actions "...." between ELSE and THEN are
carried out instead. The "ELSE ...." portion is optional, in
analogy to PASCAL.
To illustrate, here is an example:
X> 'STATE? :
X:> 'TURNED- MSG W@ IF 'ON ELSE 'OFF THEN
1X:> MSG ;
X>
Trying this new word:
X> CONSOLE STATE?
we get the response:
TURNED-ON
whereas for:
X> LIST STATE?
TURNED-OFF
PISTOL supports a number of other structures which are
analogous to the PASCAL structures:
WHILE .. DO ... ;
and
REPEAT ... UNTIL NOT .. ;
They are, respectively:
BEGIN .. IF ... REPEAT
and
BEGIN ... .. END
As in most languages, structures may be nested. In the
interest of user convenience, the prompt will indicate whether
execution is being deferred, pending completion of unfinished
structures.
PISTOL can communicate in a variety of different number
bases, as was alluded to in the section on W@ and W! . Changing
bases has been formalized by a set of defined words provided in
PBASE. These words are: BINARY(B), OCTAL(Q), DECIMAL(X), and
HEX(H); the parenthesized letter is the corresponding symbol
that appears in the prompt. Thus, in the examples described
above, the number base was decimal. Here are a few examples of
the use of other number bases:
X> HEX
H> 8 8 + =
10
H> 11 BINARY =
10001
B> 2
2 ?
***PISTOL 1.3***
B>
Whenever the system responds with ***PISTOL 1.3*** it
has performed an ABORT which resets stacks and prints this
identifying message.
You may "redefine" words, that is to say, the same name
may be used again and again. Earlier definitions that use the
word will continue to utilize the old meaning; future defini-
tions that reference the redefined word will access the new
meaning. A warning will be issued when you are redefining.
It is important to keep in mind that when new words are
defined, their names are added to the "string stack" and the
compiled code is added to the "code stack". If a recent
definition is not satisfactory, or no longer serves a need, you
may wish to "FORGET" it, so that the "string stack" and the
"code stack" are popped of this useless material. To discard
this definition AND ALL SUBSEQUENT DEFINITIONS, one should
type:
X> '<name> FORGET
To obtain the code locations and names of the last ten
definitions, type:
X> TOP10
To obtain information successively on the ten previous
defintions, type:
1X> NEXT10
A crude, line-oriented editor has been implemented
recently inside PISTOL. Try it out by typing the following
sequence of commands:
X> LI
X> 3 LI
X> 3 5 LI
X> 4 2 LI
X> 3 INPUT
3: MARY HAD
4: A LITTLE
5: LAMB.
6: <cr>
X> LI
X> 3 DELETE
X> LI
*************************
* *
* GLOSSARY *
* *
*************************
(Assuming that the definitions in PBASE have been compiled):
ARITHMETIC OPERATORS:
========== =========
+ Adds the top two items on stack.
- Subtracts the top two items on stack.
* Multiplies the top two items on stack.
/ Divides the top of stack into next to top.
MOD Calculates the remainder ("modulo") of division
of next to top by the top of stack.
/MOD Divides the next to top by the top of stack;
new next to top is the result and top is the
remainder.
MINUS Changes the sign of the top of stack.
MAX Leaves the larger of the top two members on stack.
MIN Leaves the smaller of the top two members on stack.
ABS Taks the absolute value of the top of stack.
1+ Increments the top of stack.
1- Decrements the top of stack.
W+ Increments the address on top of stack by the wordsize.
1+W! Increments the word variable addressed
by the top of stack.
W+W! Increments address variable addressed by the top of
stack by the word size.
LOGICAL OPERATORS:
======= =========
TRUE Pushes -1 on stack.
FALSE Pushes 0 on stack.
NOT [A quantity is "false" if zero; otherwise,it is "true"].
Replaces top of stack by FALSE if non-zero;
otherwise replaces top by TRUE.
LAND If either of the top two items on stack is zero,
it pushes FALSE on stack; otherwise it pushes TRUE.
("LOGICAL AND").
LOR If both items on stack are zero, it pushes FALSE;
otherwise it pushes TRUE. ("LOGICAL OR").
GT Pushes TRUE if the next to top is greater than
the top of stack.
LT Pushes FALSE if next to top is less than
the top of stack.
LTZ Pushes TRUE if top of stack is less than zero;
otherwise, it pushes FALSE.
GTZ Pushes TRUE if top of stack is greater than zero;
otherwise, it pushes false.
EQZ Pushes TRUE if the top of stack is zero; otherwise,
it pushes FALSE.
EQ Pushes TRUE if the top two members on stack were
equal; otherwise it pushes FALSE.
.. (Takes three arguments). Tests for range. The top
three items pushed on the stack should be value,
bottom of range, and top of range. If value is below
bottom or above top, FALSE is pushed on stack;
otherwise TRUE is pushed on stack.
STACK OPERATORS:
===== =========
STACK Non-destructively lists the size and contents of
the parameter stack.
RSTACK Non-destructively lists the size and contents of
the return stack.
SWAP Interchanges the top two items on stack.
DUP Pushes a duplicate copy of the top of stack.
DDUP Pushes a duplicate pair of the top two items.
DROP removes the top item from the stack.
OVER pushes the next to top on top of stack.
2OVER pushes the second to top on top of stack.
3OVER pushes the third to top on the stack.
S@ Takes the top member of stack as an index to the
interior of the stack; " 0 S@ " is equivalent
to " DUP ".
R@ Take the top of the parameter stack as an index
into the return stack.
<R Pushes the top of stack on to the return stack.
R> (opposite of " <R ").
SP pushes the current size of stack on stack.
RP pushes the current size of the return stack.
L@ similar to S@ and R@ but for the loop stack.
CASE@ similar to S@ and R@ but for the case stack.
DEFINITIONAL OPERATORS:
============ =========
CONSTANT defines a word whose name is on the top of
stack and assigns it the permanent value given
by the next to top.
VARIABLE Allocates space in RAM and defines a word whose
name is on top of stack. Later, when the name
is invoked, a pointer to the allocated space is
pushed on stack. The variable is initialized
to the next to top of stack.
: ... ; is used in creating a standard definition.
It takes the string pointed to by the top of
stack as the name of the word being defined.
The body of the word is anything that is placed
between the ":" and the ";". When the
definition extends beyond one line of text,
thesystem displays a prompt that contains a
":". Examples have been provided already in
the tutorial.
$: ... ;$ is used to create a "macro" definition. In
syntax and use its behavior is very similar to
a standard definition, as described immediately
above. However the code that is associated
with the newly defined word will be compiled
"in line" when the word is invoked, instead of
being called. Its use increases the execution
speed of any code that uses the word (since the
is no overhead from a "call"), but the
resulting code is usually longer; thus we can
choose our own preference between speed and
memory space. It is used to define "perfect
NOPs", such as in the definition of W* when the
word size is, in fact, 1. (see the beginning of
PBASE).
MEMORY RELATED OPERATORS:
====== ======= =========
W@ Fetches the contents of the word location addressed
by the top of stack (used extensively with words
defined by VARIABLE).
W! Stores at the word location addressed by the top of
stack the value next to top.
W<- equivalent to " SWAP W! "
C@ fetches the ASCII value of the character in the
string area addressed by the top of stack.
C! stores in the string area addressed by the top of
stack the character whose ASCII value is next to
the top.
R@ , S@, L@, and CASE@ (see stack operations, above)
ON sets location addressed by top of stack to TRUE.
OFF sets location addressed by top of stack to FALSE.
SYSTEM OPERATIONS:
====== ==========
COREDUMP saves an image of PISTOL's memory as a
file named with the string previously
placed on top of the stack. (for an
example see "GETTING STARTED", near the
start of this documentation).
RESTORE takes the file whose name is on the top
of stack and "restores" the memory image
that had been previously saved by a
COREDUMP command. (for an example see
"GETTING STARTED", near the start of this
documentation).
LISTFILE opens a new file with the name that was
on the top of stack. This new file can
be written into to keep a record of the
terminal session (using the boolean:
LIST). For example, to record portions
of the terminal session in a file named
"DIALOG":
X> 'DIALOG LISTFILE
X> LIST ON
.
. (recorded)
.
X> LIST OFF
.
. (not recorded)
.
X> LIST ON
.
. (recorded)
.
etc.
LOAD is used to take a PISTOL source file whose name is
pointed to by the top of stack.
SHOWCODE makes pistol display the contents of its
compile buffer after every compilation. It is
most useful when you are trying to estimate
where special patching needs to be done, such
as done by ARGPATCH (see the definitions in
PBASE for this one!). To stop showing code you
should use:
NOSHOWCODE turns off the SHOWCODE, described immediately
above.
SYSTEM VARIABLES:
====== =========
COLUMN current column that the console is believed to be
in.
TERMINAL-WIDTH current declared maximum line length of
output device (used to automatically
insert carriage return,line feeds if
output overruns line).
#LINES contains the number of lines printed since the
last carriage return was input from the keyboard.
TERMINAL-PAGE current declared maximum number of lines
user will accept. Output will pause
when #LINES equals TERMINAL-PAGE. Output
will resume, following a carriage return
from the keyboard. Typing a Q followed
by the carriage return will abort the
current activity.
TAB-SIZE is the current gap size between tab stops
(initially set to 8).
TRACE-LEVEL Boolean and number related to level of
return stack where trace action occurs.
TRACE-ADDR contains patched address for TRACE .
RAISE Boolean that determines whether lowercase input
is to be converted to uppercase (does not affect
text between double quotes).
ECHO Boolean controlling display of files being loaded.
CONSOLE Boolean controlling output to console.
LIST Boolean controlling output to the "list" file.
CURRENT contains pointer to the last defined word's definition.
.D contains current end of RAM used to store definitions.
.C contains current end of the compile buffer.
RADIX contains current base for numerical I/O.
RADIX-INDICATOR contains prompt character displaying
current base.
FENCE contains lower limit for FORGET to prevent
inadvertent excessive "loss of memory".
USER is a memory constant that is used in calculations to
locate various system variables and constants.
W is a constant that indicates the separation of word
addresses. Different virtual machines may have
different word sizes and the use of this constant makes
it possible to transport PISTOL code among such
different machines. Its use appears in many
definitions such as W+ , W- , and W* .
VERSION is the constant, 13. (for PISTOL 1.3)
I/O OPERATIONS:
=== ==========
TYI inputs a character from the keyboard and places
its ASCII value on the stack. (Console input is
buffered line-by-line).
TYO outputs a character whose ASCII value is on the stack.
TYPE types the number of characters of text specified
by the top of stack from the string area, starting at
the location pointed to by the next to top of stack.
MSG types a message pointed to by the top of stack;
starts a new line if the string is too long to
"fit" on current line(tabs not properly estimated).
MSGS takes the number on top of stack as the number of
that are next to the top of stack that are to be
output, the BOTTOM-MOST one first. For example:
'ONE 'TWO 'THREE 3 MSGS
produces:
ONETWOTHREE
If the combined length of all the strings will
not fit on the current line, a new line is started.
(tabs not properly estimated).
TAB simulates a tab; tab stops are separated by the
size specified by the variable, TAB-SIZE.
TABS outputs as many simulated tabs as is specified
by the top of stack.
INDENT advances to the coulumn specified by the top of
stack unless one is already there or beyond.
Trying to indent beyond TERMINAL-WIDTH, causes
a new line to be started instead.
ASCII converts a small number (a digit) into its ASCII
representation.
SPACE outputs a blank.
SPACES outputs as many blanks as specified by top of stack.
CR Outputs a carriage return, line-feed sequence.
IFCR does a CR if not in column 0.
= type the numerical value of the top of stack;
no leading or trailing blanks are supplied.
? types the numerical value of the RAM location
pointed to by the top of stack; equivalent to
" W@ = ".
HEX sets the base for numerical I/O to 16.
OCTAL " " " " " 8.
BINARY " " " " " 2.
DECIMAL " " " " " 10.
% is the comment delimiter; the compiler ignores
remaining text to the end of the current line. Do not
shun its use in creating PISTOL source files; PISTOL
is NOT self documenting!
===
EDITOR
======
(Line numbers change with the editing process, the first
line in the file is numbered 1, the next, 2, etc.)
NEWF resets pointers in the editor so that the edit buffer
is empty.
LISTALL lists the complete contents in the edit buffer.
LFIND take the line number from the top of stack and replaces
it with a pointer to the string pointing to the string
corresponding to that line.
LI lists portions of the edit buffer; it acts differently
depending upon the number of arguments on stack. If
there are no arguments, everthing is listed (LISTALL).
If there is a single line number on the stack, that
single line is displayed.
Listing a sequence of lines within the edit
buffer is achieved with two line numbers on the stack.
The sequence of displayed lines begins with the line
numbered by the bottom of stack. If the top of stack
contains a larger number, that number is the number
of the last line in the displayed sequence. Otherwise,
if it is a smaller number, it is used to specify the
total number of lines in the sequence.
INPUT takes the line number on the top of stack and allows
the user to enter (insert above the old line specified)
as many new lines as desired. Completion of input is
signified by supplying an empty line (an immediate
carriage return).
DELETE takes a single line number and deletes that line from
the edit buffer.
DELETES deletes several lines. How many is specified by the
top of stack. The location by the next to top.
OPENR takes a string pointed to by the top of stack and
opens that named file for reading. It is the user's
responsibility to make sure that the named file
exists already. If another file was opened for reading
previously, it is closed by this new OPENR.
OPENW takes a string pointed to by the top of stack and
opens that named file for writing.
(*** It will DESTROY any file that already exists
with that name!!!****)
READLINE reads a line of text from the file that has been
opened for reading (from OPENR) and places it into
the input line buffer, pointed to by LINEBUF. This
text will be overwritten by the next input line so
it should be used prior to the next input line.
WRITELINE writes that line that is in STRINGS (usually in
the edit buffer) whose pointer is on the top of the
stack. The writing is into the file that was opened
for writing by OPENW.
READ takes the number of lines specified by the top of
stack from the file specified by the last OPENR and
appends them to the end of text in the edit buffer.
WRITE takes the number of lines specified by the top of
stack from the beginning of the text in the edit
buffer and places them at the end of the file
specified in the last OPENW.
FINISH is used to end the edit session. It appends both
the contents of the editbuffer to the file
specified in the last OPENW and the remaining
lines of the file specified by the last OPENR.
ITERATION
=========
PISTOL provides four means for iterative execution
of a sequence of words, namely:
BEGIN ... END
executes words between BEGIN and END until a
condition is satisfied.
BEGIN ... IF ... REPEAT
is similar to BEGIN ... END except the condition is
tested at the beginning of the loop; iteration terminates
when the tested condition is false.
DO ... LOOP
executes the words between DO and LOOP, running an
index [accessible as "I"] from a lower to upper limit,
incrementing by one each time.
DO ... n +LOOP
executes the words between DO and +LOOP, running
an index from a lower to an upper limit, incrementing
by n each time.
Iterations may be nested subject to the normal
restrictions on overlapping ranges, i.e. any iteration which
is initiated within the range of another iteration must be
terminated within that same range. PISTOL has implemented a
"check stack" to enforce this syntax rule and, as an aid to
interactive programming, displays this stack in the prompt.
We shall describe these four iterative structures in
more detail now:
BEGIN ... END
=============
The BEGIN ... END syntax permits the user to execute
a sequence of words and then, depending upon a computed
logical variable, either loop back or continue on:
BEGIN word1 word2 .... wordm END
The sequence word1, word2, ... is executed once.
When END is reached, the top of the stack is popped and
tested. If it is true (non-zero) then control passes to the
word following END. If it is false (zero) then control
passes back to the word following BEGIN.
An example:
'EXAMPLE : BEGIN 1- DUP DUP = EQZ END DROP ;
defines the word EXAMPLE which might be called
as follows:
X> 5 EXAMPLE
4 3 2 1 0
Each time through the loop, the top of the stack
(initially the number 5) is decremented, printed and
compared to zero. If it is not zero, the loop is
repeated; the loop terminates when it becomes zero.
BEGIN ... IF ... REPEAT
=======================
BEGIN ... IF ... REPEAT is similar to BEGIN ... END
except that the test is at the beginning of the loop. The
words between BEGIN and IF are executed. The top of the
stack is then popped and tested. If it is true (non-zero)
the words between IF and REPEAT are executed and control
passes back to the first word after BEGIN. If the top of the
stack had been tested false (zero) control would have passed
to the word following REPEAT.
An example:
'LENGTH : 0 BEGIN SWAP DUP IF W@ SWAP 1+ REPEAT UNDER ;
might be used to determine the length of a chain of
pointers terminated by zero. The initial pointer
would be placed on the stack and LENGTH would be
invoked. If one could not place the test at the
beginning of the iteration, one would have a problem
with a zero length chain (a zero initially on the
stack).
DO LOOPS
========
A DO LOOP facility is provided by PISTOL for indexing
through a sequence of words. There are two forms of DO LOOP:
HIGH LOW DO word1 word2 ... wordn LOOP
HIGH LOW DO word1 word2 ... wordn STEP +LOOP
The limits HIGH and LOW (the top two stack entries) are
compared. If HIGH is less than or equal to LOW, control passes
to the word following LOOP or +LOOP. Otherwise, the sequence
word1, word2, ... is executed. LOOP causes the lower limit,
LOW to be incremented and compared to the upper limit, HIGH.
If LOW is greater than or equal to HIGH, the loop is
terminated. Otherwise another iteration is performed. The
+LOOP is identical to LOOP except that the LOW is incremented
by the word on top of stack, STEP. Normally, STEP would be a
positive number.
Within the range of a loop, the current value of the
loop index is available by using "I". If DO LOOPs are nested,
I contains always the value of the innermost index. The next
outer indices are available using the words, J and K. The word
I' is used to obtain the value of (HIGH+LOW-I-1). This is used
to run an index backwards from HIGH-1 to LOW . The words J'
and K' are similarly defined. When parenthesis (iteration
brackets) are nested with DO LOOPs, they count as one level of
indexing. When I is used within the range of an iteration
bracket the current iteration count (which runs from its
initial value downwards to one) is placed on stack.
The word EXIT causes the innermost loop in which
it is embedded to terminate unconditionally.
Some examples:
5 0 DO I = LOOP
causes the numbers 0 to 4, inclusive to be typed out.
5 0 DO 5 0 DO J 5 * I + = LOOP CR LOOP
causes the numbers 0 through 24 inclusive to be
typed out as 5 lines of 5 numbers each.
5 0 DO I' = LOOP
causes the numbers 4 ... 0, inclusive to be output.
0 21 1 DO I + DUP = 2 +LOOP DROP
types out the first 10 perfect squares starting with 1.
When using I' (or J' or K') in conjunction with +LOOP,
HIGH should be replaced by HIGH - STEP + 1 if it is desired
to produce the same set of indices as with I . For example:
X> 24 0 DO I = 4 +LOOP
0 4 8 12 16 20
X> 24 0 DO I' = 4 +LOOP
23 19 15 11 7 3 1
X> 24 4 - 1+ 0 DO I' = 4 +LOOP
20 16 12 8 4 0
CONDITIONALS
============
PISTOL has a powerful IF ... ELSE ... THEN construction
which allows moderately complex logical tests to be performed.
In addition, for more complex situations an OFCASE ... ENDCASE
n-branch construction is provided also [the"CASE"construction].
Conditionals may be nested within each other and within
iteration loops with the same restrictions that apply to
iterations. The check check enforces that proper nesting is
maintained and will issue fatal error messages otherwise. The
prompt provides the user with information on the current
nesting status.
For purposes of the conditional, "true" is considered
to be any non-zero value; "false" is any zero value. [The
"best" true value is -1, viz. all 1's in binary, in that
"-1 N AND" will be always "true" unless N is "false".]
val IF true1 true2 ... ELSE false1 false2 ... THEN
The top of stack, val is tested and
if true (non-zero), the words true1,true2,...
are executed; control passes then to the word
following THEN,
otherwise, if false (zero) control passes to false1 ,
false2 ... ; control passes then to the word
following THEN,
Two examples:
'ABS : DUP LTZ IF MINUS THEN ;
defines the word ABS which replaces the top of stack
with its absolute value.
'MAX : DDUP GT IF DROP ELSE UNDER THEN ;
defines the word MAX which compares the top two stack
entries and leaves the larger of the two.
The CASE construction simplifies many programs where
the value of a variable is used to choose among many
possibilities. Its syntax is necessarily more complex:
value OFCASE <test1> C: <action1> ;C
<test2> C: <action2> ;C
. . . .
. . . .
<testn> C: <actionn> ;C
ENDCASE
The liberal use of carriage returns and tabs in the
coding improves readability, but is not required by the syntax.
OFCASE saves value and replaces it on the stack before each
test. If <test1> is true then <action1> (which may be any
number of words) is carried out and control skips to the first
word after ENDCASE. Otherwise, if <test1> is false, value is
again placed on stack and <test2> performed; if it proves true
then <action2> is done and control passes to the word following
ENDCASE, etc. Thus the first successful test selects the action
performed.
If every test, including <testn> is false, control
reaches ENDCASE and a fatal error message is generated.
An example should clarify this (notice how the prompt changes):
X> 'SPELL : OFCASE
X:C> 0 EQ C: 'ZERO ;C
X:C> 1 EQ C: 'ONE ;C
X:C> 2 EQ C: 'TWO ;C
X:C> 2 GT C: 'MANY ;C
X:C> ENDCASE MSG ;
When testing this definition one finds:
X> 2 SPELL
TWO
X> 3 SPELL
MANY
X> -1 SPELL
CASE EXECUTION ERROR AT 11672 FOR THE VALUE -1 PISTOL
The fatal error message provides the address of ENDCASE
and the value that created the problem.
OFCASE does not use the loop stack but uses its own
"CASE" stack; the words I , J , K , I' , J' , K' and EXIT will
properly access the DO...LOOP counters when intermixed with
this structure. The words, ICASE and JCASE, access the case
variable of the innermost OFCASE structure and the next to
innermost OFCASE structure, respectively. They are somewhat
analogous to the words I and J of the DO LOOP structure.
RECURSION
=========
Normally, a procedure cannot invoke itself because it
is compiled before its own name is recorded; therefore the
word RECURSE is provided as a "stand-in" for the name of the
procedure being defined. This word also provides the means for
recursing within the compile buffer, itself.
For recursion which requires forward references one has
to make an extra effort (as one does in PASCAL, which requires
the use of the reserved word, FORWARD). In PISTOL we must
define first a variable, say, FW, which is destined to contain
the address of the forward reference. One defines then those
routines that invoke the forward-referenced routine with the
code:
... FW W@ EXEC ...
Eventually, one is in the position to define the routine that
is needed to complete the recursive circle, say, LAST. After
defining LAST the recursive loop is established with:
'LAST ADDRESS FW W!
which records the address of LAST in the variable, FW .
MISCELLANEOUS AIDS
============= ====
To name the ten most recent definitions one should
type: TOP10 . It will display the information and leave on
stack the address of the 11th most recent definition. To name
further definitions, one can type NEXT10 which takes the
address off of the stack and names the next ten definitions.
In turn, it leaves the address of the 21th most recent
definition on stack.
One can dis-assemble a definition using the word, DIS .
For example:
X> 'TOP10 DIS
produces the result:
'TOP10 : CURRENT W@ NEXT10 ;
Not all definitions are disassembled so faithfully.
To examine the stack in a non-destructive manner, one
can use the word, STACK. It will display the number of items
in the stack and their values. For testing definitions it is
useful to check the stack before and after the definition has
been invoked.
If the definition appears to be faulty, it is often
useful to debug it by tracing the program flow through the
definition and the state of the stack at intermediate steps.
This can be accomplished automatically by the use of the word,
TRACE. For example, to trace the action of DDUP one can type:
X> 1 2 'DDUP TRACE
To examine the contents of the return stack in a
non-destructive manner, one can use the word, RSTACK .
SHOWCODE and
NOSHOWCODE permit you to see (and to stop seeing!) the
generated code in the compile buffer after each
compilation. It can be educational as well as
important for certain debugging tasks.
'name LISTFILE
LIST ON
.
.
LIST OFF permit recording details of the session in a
file for later review. It can be a lot more
convenient then using the printer all the time.
or testing